Home |
| Latest | About | Random
# Week 2 Wednesday. Dear Class, Today we discuss the `string` class (section 6), as well as the unsigned types `unsigned int` and `size_t` (sections 5.8 and 5.9). On Friday we discuss the input buffer (section 7). **Reminders** - HW1 is due this Thursday at 10pm. `#include` the header for every function you use (look them up on cppreference), and be careful with `int` vs `double` and with explicit casting via `static_cast(...)`. And as we have not technically learned about the control flows `if` and `else`, try to solve the problems avoiding using those. You will gain an even better understanding! - If you are on the waitlist (just a couple of you), I will try to resolve it sometimes Friday. - If you have final exam conflict with another class's exam, see Announcement in BruinLearn. - This week finish reading sections 5, 6, 7. Ok, you can do it! -- B. . . . . . . . ## The mystery continues. ```cpp #include #include using namespace std; int main(){ string s = "Hello"; // This is one way to create a string cout << s.length() << endl; cout << s.length() - 10 << endl; return 0; } ``` How curious, 5 minus 10 is....??! . . . . . . ## `std::string` class. Storing text. - So far our variables hold numbers, `bool`s, and single `char`s. To store **text** we use the `std::string` class. - A **class** is a souped-up data type. Its superpower is **member functions** (or "methods") that can *do* things. (Later we will learn how to create [[1-teaching/ucla-2026-fall-pic-10a/class-sneak-peak|our own data type]]) - `#include `, even if your IDE lets you skip it. Four ways to construct a `string` ```cpp #include #include using namespace std; int main(){ // different ways to create a string string s1 = "Hello"; string s2("Can you hear me?"); string s3(10, '!'); string s0; cout << s1 << endl; cout << s2 << endl; cout << s3 << endl; cout << s0 << endl; // We can also use cin to take user input as a string. // But there are some nuances that we will discuss next time. // string name; // cout << "What is your name? "; // cin >> name; // cout << "Aha, your name is " << name; return 0; } ``` - `s1`, `s2`, `s3`, `s0` are **instances** of the `string` class. - Unlike an uninitialized `int`, the default-constructed `s0` is fine: it is the **empty string**. - A string literal `"Hello"` is **not** a `string`. **User input.** `cin >> name;` works with a `string` (try it, including typing a space; we explain the space on Friday). - The `string` class data type has **member functions**, that can perform actions. Member functions are called with a **dot** `.` (*member access operator*): ``` [instance].[member function](arguments) ``` Try these on `s2`: - `.length()` - `.at(i)` - `.substr(i)` - `.substr(i, j)` **Careful:** `substr(i, j)`: `j` is a **count** (at most `j` characters), not an end index. - Functions like `pow`, `round`, `max`, `min` are **free functions** (no dot). **READ Section 6.3**: what `substr` does at the edges (count too big is fine; position equal to length gives the empty string; position past the length is a runtime error). . . . . . . ## Indexing. ``` s     =  H  e  l  l  o index =  0  1  2  3  4 ``` - Counting starts at `0`: say **0-th**, **1-th**, **2-th**. `s.at(0)` and `s[0]` are both `'H'`. - The last character is `s[s.length() - 1]`. - **`at` vs `[ ]`:** `at` checks bounds and gives a **runtime error** if out of range. `[ ]` gives **undefined behavior**. **Example.** (build error, runtime error, or undefined behavior?). With `string s = "abcdef";` 1. `cout << s[8];` 2. `cout << s.at(8);` **Practice.** Given a name in lower case `string my_name = "joe";`, capitalize the first letter. Try to do it in a way that accounts for any lower case name. **BUT WAIT. What is the type of the number going into `at(i)`, and the type of the number coming out of `length()`?** . . . . . . . ## `unsigned int` and `size_t`: never negative - An `int` is about -2 billion to 2 billion. - An `unsigned int` is about 0 to 4 billion (2^32 - 1). - But what happens at the **edge**? ```cpp #include using namespace std; int main() { unsigned int x = 0; cout << x << endl; x--; cout << x << endl; // ??? unsigned int y = -1; cout << y << endl; // ??? y++; cout << y << endl; // ??? int i = -1; unsigned int u = 0; cout << i + u << endl; // ??? return 0; } ``` - This **wraparound** is *defined* for `unsigned int` (works modulo 2^32) but *undefined* for `int`. - **Mixed arithmetic:** when an `int` meets an `unsigned int`, the `int` is converted to unsigned first. So `-1 + 0u` is `4294967295`. - **READ 5.8.2 and 5.8.3**, starting with **Definition 5.8.2**, on casting between `int` and `unsigned int`. - Rule of thumb: unsigned types exist because **sizes and indices** are unsigned. For ordinary quantities (money, temperatures, differences), prefer `int` or `double`. ### `size_t` - `size_t` (from ``) is an unsigned type big enough to describe the size of any object on the computer. On 64-bit builds it is 0 to about $18 \times 10^{18}$ (18 quintillion). Some machines are 32-bits, beware! - It is the right type to store **lengths** and **indices**. It wraps around like any unsigned type. ```cpp size_t len = s.length(); size_t index = 0; ``` Can you now figure out our original mystery? - `s.length()` is a `size_t`, so `s.length() - 10` is `5 - 10` computed as an **unsigned** number. It wraps around to `18446744073709551611`. The same happens with the empty string: `e.length() - 1` is `18446744073709551615`, not `-1`. - Remember `18446744073709551615`. It is `static_cast(-1)`. Sometimes this is used to indicate something is not found. . . . . . . . ## Member functions and operations with `string`. - Besides using computers to do numerical calculations, another common endeavor is parsing and manipulating text (reading and cleaning up data, extracting information, scraping websites, etc.) - Let `s`, `t` be `string`s: | Expression | What it gives | | ----------------- | ------------------------------------------------------- | | `s.at(i)`, `s[i]` | the `i`-th character | | `s.length()` | number of characters (a `size_t`) | | `s.substr(i)` | substring from index `i` to the end | | `s.substr(i, j)` | substring from `i` with at most `j` characters | | `s.find(t)` | index of the **first** occurrence of `t` in `s` | | `s.find(t, i)` | first occurrence at index `i` or later | | `s.rfind(t)` | index of the **last** occurrence of `t` in `s` | | `s.rfind(t, i)` | last occurrence starting at or before index `i` | | `s + t` | a new `string`: `s` followed by `t` (**concatenation**) | | `stod(s)` | converts `s` to a `double` | **1. `find` and `rfind` return a `size_t`.** Store them in a `size_t`, not an `int`. ```cpp #include #include using namespace std; int main() { string fruit = "banana"; cout << fruit.find("na") << endl; // 2 cout << fruit.rfind("na") << endl; // 4 cout << fruit.find("ma") << endl; // 18446744073709551615 ... remember our friend? return 0; } ``` - When nothing is found you get `string::npos`, which is `static_cast(-1)`. That is the wraparound number from the mystery earlier. Test with `== string::npos`. ```cpp #include #include using namespace std; int main() { string x = "birdcowfish"; size_t p; p = x.rfind("cow"); cout << p << endl; p = x.rfind("cow", 4); cout << p << endl; p = x.rfind("cow", 3); cout << p << endl; return 0; } ``` **2. `+` needs a `string` on at least one side.** ```cpp #include #include using namespace std; int main() { string a = "Hello"; string b = a + ", world!"; cout << b; // OK // string c = "Hello" + ", world!"; // BUILD ERROR: two string literals return 0; } ``` **3. `stod` reads the longest numeric prefix and ignores the rest.** ```cpp cout << stod("0.125") * 4.5 << endl; // 0.5625 cout << stod("-3.5abc") << endl; // -3.5 cout << stod("+0.1875=7") << endl; // 0.1875 // stod("abc") is a runtime error ``` **4. Avoid magic numbers.** Try not to hard code special lengths and positions with literal numerical values like `4`, but instead compute them by `.length()` or `.find(t,j)`. And compute them out of `const string` to avoid manual counting. (See DNA example below.) **READ. Find on cppreference `+=`, `push_back`, `pop_back` for `string`.** . . . . . ---- **Postscript.** **We ended here. Try the following DNA analysis example at home!** ## Example. DNA analysis. Suppose you are given this piece of DNA sequence (in letters `G, A, T, C`): ```cmd AATCGCCACCGGAAGCAAGAGGGAGCTGGTCTGGATATCGTGATCCTTTGGGTAAAGATTGGAGTTAACACCTTCGGTGTCCAGCAATGCTAATACGGTTGGGAAACATATAGGACTCGCGGCAGCTGGTGGATCGTGACAAATAAAGGGAAATCCCGTTCTCGGGTCAAGTAGTTCTCTCATGAACCAAGCCGACCTCAGAGGTACGGATGTAAAGGTTACCAGGGCTATCGGAACTAGATCGGTACAGGACGTGGACAGTATGCAACCATAATGCGTCATTAATGGGTTTATGAGCCT ``` **Start by:** Initialize a const string `const string dna = "AATC...";` (Read the note on `const` below) **Tasks. (Try at home if we didn't finish in class)** 1. Every block of 5 letters is a *section*. Print the first and the sixth section (English sense!). 2. `GGGAAA` is cancerous. Hint: initialize a const string `const string cancerous = "GGGAAA";` - Find the 4 letters right after the first occurrence of this cancerous sequence. - Is there a second occurrence? A third? (What does `find` give you?) - What if instead `CAAATAAA` is cancerous? Repeat the previous two subparts. 1. Make a new DNA sequence with the first occurrence of `CAAATAAA` removed. 2. The sub-DNA strictly between the first `GGGAAA` and the last `CATAA`. 3. From `"temp=96F;hum=53%"`, get two `double`s and print their sum. [[1-teaching/ucla-2026-fall-pic-10a/DNA-answers|Suggested answer to above.]] ## A note on `const`. Recall `const` on a variable "locks it" and make it read only. Here both `dna = "...."` and `cancerous = "GGGAAA"` are fixed pieces of information. They should not get changed during the program. And by putting `const` we can enforce that. ```cpp #include #include using namespace std; int main() { const string dna = "CCCCCGGGAAAACCCCCCC"; const string cancerous = "GGGAAA"; // cancerous = "AAA"; // BUILD ERROR: can't assign to a const // cancerous[0] = 'T'; // BUILD ERROR: can't modify its characters cout << cancerous.length() << endl; // fine: just looking cout << dna.find(cancerous) << endl; // fine: just looking cout << dna.substr(3); // fine: we are making a new string return 0; } ``` .